using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using COLLADA;
using System.IO;
using GingerBeard.Utilities;
using System.Diagnostics;

namespace ColladaToData
{
	public class Exporter
	{
		private Document doc;
		private String platform;
		private Data.RenderData renderData;
		private Dictionary<UInt32, Int32> gpuProgramCache = new Dictionary<UInt32, Int32>();
		private Dictionary<UInt32, Int32> textureCache = new Dictionary<UInt32, Int32>();

		private class MaterialRecord
		{
			public Int32 idxMaterial = -1;
			public Dictionary<String, KeyValuePair<String, String>> bindings = new Dictionary<String, KeyValuePair<String, String>>();
		}

		private class BoundGeometryRecord
		{
			public Int32 idxModel = -1;
			public Dictionary<String, KeyValuePair<String, String>> bindings = new Dictionary<String, KeyValuePair<String, String>>();
		}

		private class GeometryRecord
		{
			public List<BoundGeometryRecord> boundGeometryRecords = new List<BoundGeometryRecord>();
		}

		private Dictionary<Document.Geometry, GeometryRecord> geometryRecords = new Dictionary<Document.Geometry, GeometryRecord>();
		private Dictionary<Document.Material, MaterialRecord> materialrecords = new Dictionary<Document.Material, MaterialRecord>();

		public static void Convert(Document inDoc, Stream outStream, String inPlatform)
		{
			new Exporter(inDoc, outStream, inPlatform);
		}

		private Exporter(Document inDoc, Stream outStream, String inPlatform)
		{
			doc = inDoc;
			renderData = new Data.RenderData();
			platform = inPlatform;

			Conditioner.ConvexTriangulator(doc);
			Conditioner.Reindexor(doc);

			Document.VisualScene scene = doc.dic[doc.instanceVisualScene.url.Fragment] as Document.VisualScene;
			if (scene == null) throw new Exception("Document doesn't contain a visual scene");

			foreach (Document.Node node in scene.nodes)
				ProcessSceneNode(node);

			// Now save out the renderdata block
			MemoryStream memStream = null;
			UInt32 renderDataLength = 0;
			using (memStream = new MemoryStream())
			{
				using (BinaryWriter writer = new BinaryWriter(memStream))
				{
					renderData.Write(writer);
					renderDataLength = (UInt32)memStream.Length;
				}

				using (BinaryWriter writer = new BinaryWriter(outStream))
				{
					Data.DataBlockHeader mainHeader = new Data.DataBlockHeader();
					mainHeader.Id = GingerBeard.Utilities.Crc.GetInstance().GenerateCrc("DATA");
					mainHeader.SizeInBytes = 0;
					mainHeader.Write(writer);

					Data.DataBlockHeader renderDataHeader = new Data.DataBlockHeader();
					renderDataHeader.Id = GingerBeard.Utilities.Crc.GetInstance().GenerateCrc("RDAT");
					renderDataHeader.SizeInBytes = renderDataLength;
					renderDataHeader.Write(writer);

					writer.Write(memStream.GetBuffer());
				}
			}
		}

		private void ProcessSceneNode(Document.Node node)
		{
			// Extract transform

			// Process any instances attached to this node
			if (node.instances != null)
			{
				foreach (Document.Instance instance in node.instances)
				{
					if (instance is Document.InstanceGeometry)
					{
						ProcessInstanceGeometry(instance as Document.InstanceGeometry);
					}
				}
			}

			// Process children
			if (node.children != null)
			{
				foreach (Document.Node child in node.children)
				{
					ProcessSceneNode(child);
				}
			}
		}

		private void ProcessInstanceGeometry(Document.InstanceGeometry instanceGeometry)
		{
			Document.Geometry targetGeometry = (Document.Geometry)doc.dic[instanceGeometry.url.Fragment];

			// Lookup or create the master record for this geometry type
			GeometryRecord geometryRecord = null;
			if (geometryRecords.ContainsKey(targetGeometry))
				geometryRecord = geometryRecords[targetGeometry];
			else
			{
				geometryRecord = new GeometryRecord();
				geometryRecords[targetGeometry] = geometryRecord;
			}

			// Collect bindings for this instance
			BoundGeometryRecord newBoundGeometryRecord = new BoundGeometryRecord();
			foreach (String materialInstanceName in instanceGeometry.bindMaterial.instanceMaterials.Keys)
			{
				Document.InstanceMaterial materialInstance = instanceGeometry.bindMaterial.instanceMaterials[materialInstanceName];
				newBoundGeometryRecord.bindings.Add(materialInstance.symbol, new KeyValuePair<String,String>("mat", materialInstanceName));

				if (materialInstance.bindVertexInputs != null)
				{
					foreach (Document.InstanceMaterial.BindVertexInput bvi in materialInstance.bindVertexInputs)
					{
						newBoundGeometryRecord.bindings.Add(bvi.semantic, new KeyValuePair<String, String>("vert", bvi.inputSemantic + bvi.inputSet.ToString()));
					}
				}
			}

			Int32 idxModel = -1;
			
			// Have a look at previous bindings for the geometry and see if we've already seen this combination
			foreach (BoundGeometryRecord boundGeometryRecord in geometryRecord.boundGeometryRecords)
			{
				// Lets assume it's going to match
				idxModel = boundGeometryRecord.idxModel;

				foreach (String bindingKey in boundGeometryRecord.bindings.Keys)
				{
					if (newBoundGeometryRecord.bindings.ContainsKey(bindingKey))
					{
					}
					else
					{
						idxModel = -1;
						break;
					}
				}
			}

			// If not, create it
			if (idxModel == -1)
			{
				idxModel = ProcessGeometry(targetGeometry, newBoundGeometryRecord);
			}

			// Create a new instance
			String instanceName = instanceGeometry.name;
			if (instanceName == null)
				instanceName = (new Random()).Next().ToString();
			Data.Instance newInstance = new Data.Instance();
			newInstance.idxModel = idxModel;
			newInstance.name = Crc.GetInstance().GenerateCrc(instanceName);
			newInstance.idxTransform = -1; // TODO: extract transform
			newInstance.flags = 0; // TODO: extract flags
			renderData.AddObject(newInstance);
		}

		private Int32 ProcessGeometry(Document.Geometry targetGeometry, BoundGeometryRecord boundGeometryRecord)
		{
			Data.Model newModel = new Data.Model();
			Int32 idxModel = renderData.AddObject(newModel);
			newModel.flags = 0;
			newModel.name = Crc.GetInstance().GenerateCrc(targetGeometry.name);
			newModel.numLods = 1;

			Data.ModelLod newLod = new Data.ModelLod();
			newModel.idxFirstLod = renderData.AddObject(newLod);
			newLod.flags = 0;

			List<Data.Submodel> submodels = new List<Data.Submodel>();

			foreach (Document.Primitive primitive in targetGeometry.mesh.primitives)
			{
				Data.Submodel submodel = ProcessSubmodel(primitive, boundGeometryRecord);
				submodel.idxVertexBuffer = -1;
				submodels.Add(submodel);

				Dictionary<String, Document.Input> inputs = new Dictionary<string, Document.Input>();
				foreach (Document.Input input in Util.getAllInputs(doc, primitive))
				{
					inputs[input.semantic] = input;
					if (input.semantic == "POSITION")
					{
						Data.Buffer vertexBuffer = new Data.Buffer();
						submodel.idxVertexBuffer = renderData.AddObject(vertexBuffer);
						float[] data = (((Document.Source)input.source).array as Document.Array<float>).arr;
						Byte[] initData = ByteConversion.ToByteArray(data);
						vertexBuffer.idxInitData = renderData.AddBoxedObjects(initData);
						vertexBuffer.numInitDataBytes = (UInt32)initData.Length;
						vertexBuffer.numElements = (UInt32)((Document.Source)input.source).accessor.count;
						vertexBuffer.format = Data.BufferFormat.VertexStream;

						UInt32 curOffset = 0;
						Data.BufferDeclaration decl = new Data.BufferDeclaration();
						vertexBuffer.idxDeclaration = renderData.AddObject(decl);

						UInt32[] floatTypes = 
						{
							Crc.GetInstance().GenerateCrc("Float1"),
							Crc.GetInstance().GenerateCrc("Float2"),
							Crc.GetInstance().GenerateCrc("Float3"),
							Crc.GetInstance().GenerateCrc("Float4")
						};

						List<Data.BufferDeclarationElement> elements = new List<Data.BufferDeclarationElement>();
						foreach (Document.Input bufinput in Util.getAllInputs(doc, primitive))
						{
							if (((Document.Source)bufinput.source).accessor.parameters[0].type != "float")
								throw new Exception("Found a vertex data source with non-float data. We don't support this yet!");

							Data.BufferDeclarationElement element = new Data.BufferDeclarationElement();
							element.name = Crc.GetInstance().GenerateCrc(bufinput.semantic);
							element.numElements = (UInt32)((Document.Source)bufinput.source).accessor.ParameterCount;
							element.type = floatTypes[element.numElements - 1];
							element.semantic = Crc.GetInstance().GenerateCrc(bufinput.semantic);
							element.offset = (UInt32)curOffset;
							elements.Add(element);
							curOffset += (UInt32)(((Document.Source)bufinput.source).accessor.ParameterCount * 4);
						}

						decl.stride = curOffset;
						decl.idxFirstElement = renderData.AddObjects(elements);
						decl.numElements = (UInt32)elements.Count;
					}
				}
			}

			newLod.idxFirstSubmodel = renderData.AddObjects(submodels);
			newLod.numSubmodels = (UInt32)submodels.Count;

			return idxModel;
		}

		private Data.Submodel ProcessSubmodel(Document.Primitive primitive, BoundGeometryRecord boundGeometryRecord)
		{
			if (!(primitive is Document.Triangle))
				throw new Exception("Unsupported primitiveType " + primitive.GetType().ToString());

			Data.Submodel submodel = new Data.Submodel();
						
			submodel.numIndices = (UInt32)primitive.count * 3;
			submodel.firstVertex = UInt32.MaxValue;
			submodel.numVertices = UInt32.MinValue;
			submodel.firstIndex = 0;

			String materialBinding = boundGeometryRecord.bindings[primitive.material].Value;
			Document.Material targetMaterial = doc.dic[materialBinding] as Document.Material;
			submodel.idxMaterial = ProcessMaterial(targetMaterial, boundGeometryRecord);

			Data.Buffer indexBuffer = new Data.Buffer();
			submodel.idxIndexBuffer = renderData.AddObject(indexBuffer);
			indexBuffer.numElements = submodel.numIndices;
			indexBuffer.format = Data.BufferFormat.Index16Stream;
			for (int i = 0; i < primitive.p.Length; ++i)
			{
				submodel.firstVertex = Math.Min(submodel.firstVertex, (UInt32)primitive.p[i]);
				submodel.numVertices = Math.Max(submodel.numVertices, (UInt32)primitive.p[i]);
				if (primitive.p[i] > 65536)
				{
					indexBuffer.format = Data.BufferFormat.Index32Stream;
				}
			}
			submodel.numVertices = submodel.numVertices - submodel.firstVertex;

			switch (indexBuffer.format)
			{
				case Data.BufferFormat.Index16Stream:
					{
						UInt16[] indices = new UInt16[primitive.p.Length];
						for (int i = 0; i < primitive.p.Length; ++i)
						{
							indices[i] = (UInt16)primitive.p[i];
						}
						Byte[] initData = ByteConversion.ToByteArray(indices);
						indexBuffer.idxInitData = renderData.AddBoxedObjects(initData);
						indexBuffer.numInitDataBytes = (UInt32)initData.Length;
						break;
					}
				case Data.BufferFormat.Index32Stream:
					{
						UInt32[] indices = new UInt32[primitive.p.Length];
						for (int i = 0; i < primitive.p.Length; ++i)
						{
							indices[i] = (UInt32)primitive.p[i];
						}
						Byte[] initData = ByteConversion.ToByteArray(indices);
						indexBuffer.idxInitData = renderData.AddBoxedObjects(initData);
						indexBuffer.numInitDataBytes = (UInt32)initData.Length;
						break;
					}
				default:
					{
						break;
					}
			}
			
			return submodel;
		}

		private Int32 ProcessMaterial(Document.Material targetMaterial, BoundGeometryRecord boundGeometryRecord)
		{
			Int32 idxMaterial = -1;

			if (materialrecords.ContainsKey(targetMaterial))
			{
				MaterialRecord materialRecord = materialrecords[targetMaterial];

				// Compare bindings
				List<String> tmp = new List<String>();
				foreach (String key in boundGeometryRecord.bindings.Keys)
				{
					tmp.Add(key + boundGeometryRecord.bindings[key]);
				}

				foreach (String key in materialRecord.bindings.Keys)
				{
					tmp.Remove(key + materialRecord.bindings[key]);
				}
			}

			if (idxMaterial == -1)
			{
				Data.Material newMaterial = new Data.Material();
				idxMaterial = renderData.AddObject(newMaterial);
				newMaterial.name = Crc.GetInstance().GenerateCrc(targetMaterial.id);

				Document.Effect effect = (Document.Effect)doc.dic[targetMaterial.instanceEffect.Fragment];
				Document.ProfileCOMMON profile = null;
				foreach (Document.IProfile tmpProfile in effect.profiles)
				{
					if (tmpProfile is Document.ProfileCOMMON)
					{
						profile = (Document.ProfileCOMMON)tmpProfile;
						break;
					}
				}

				if (profile == null)
					throw new Exception("No common profile found in material " + targetMaterial.name);

				Document.SimpleShader shader = ((Document.ProfileCOMMON)profile).technique.shader;

				Data.RenderMethod newRenderMethod = new Data.RenderMethod();

				List<Data.Parameter> materialParams = new List<Data.Parameter>();

				List<String> requiredShaderBits = new List<String>();

				if (shader.diffuse is Document.Texture)
				{
					Document.Texture tex = (Document.Texture)shader.diffuse;

					String textureChannel = null;
					if (boundGeometryRecord.bindings.ContainsKey(tex.texcoord))
						textureChannel = boundGeometryRecord.bindings[tex.texcoord].Value;
					else
						textureChannel = CrappyRemap(tex.texcoord);

					requiredShaderBits.Add("#define DEF_DIFFUSE_TEXTURE");
					requiredShaderBits.Add("#define DEF_ENABLE_" + textureChannel);
					requiredShaderBits.Add("#define DIFFUSE_TEXTURE_TEXCOORD TEX_" + textureChannel);

					string sampler = ((Document.Texture)shader.diffuse).texture;
					string imagePath = null;

					if (profile.newParams.ContainsKey(sampler))
					{
						string surface = ((Document.Sampler2D)profile.newParams[sampler].param).source;
						string image = ((Document.Surface)profile.newParams[surface].param).initFrom;
						imagePath = ((Document.Image)doc.dic[image]).init_from.Uri.LocalPath;
					}
					else
					{
						imagePath = ((Document.Image)doc.dic[sampler]).init_from.Uri.LocalPath;
					}

					Int32 idxTexture = CreateTexture(imagePath);

					Data.Parameter param = new Data.Parameter();
					param.name = Crc.GetInstance().GenerateCrc("g_DiffuseTexture");
					param.type = Crc.GetInstance().GenerateCrc("Texture");
					param.idxInitData = idxTexture;
					materialParams.Add(param);
				}
				else if (shader.diffuse is Document.Color)
				{
					Document.Color color = (Document.Color)shader.diffuse;
					Data.Parameter param = new Data.Parameter();
					param.name = Crc.GetInstance().GenerateCrc("g_DiffuseColour");
					param.type = Crc.GetInstance().GenerateCrc("Float4");
					float[] floats = { color.floats[0], color.floats[1], color.floats[2], color.floats[3] };
					param.idxInitData = renderData.AddBoxedObjects(ByteConversion.ToByteArray(floats));
					materialParams.Add(param);
					requiredShaderBits.Add("#define DEF_DIFFUSE_FIXED");
				}

				if (shader.ambient is Document.Texture)
				{
					Document.Texture tex = (Document.Texture)shader.ambient;
					String textureChannel = boundGeometryRecord.bindings[tex.texcoord].Value;
					requiredShaderBits.Add("#define DEF_AMBIENT_TEXTURE");
					requiredShaderBits.Add("#define DEF_ENABLE_" + textureChannel);
					requiredShaderBits.Add("#define AMBIENT_TEXTURE_TEXCOORD TEX_" + textureChannel);

					string sampler = ((Document.Texture)shader.ambient).texture;
					string surface = ((Document.Sampler2D)profile.newParams[sampler].param).source;
					string image = ((Document.Surface)profile.newParams[surface].param).initFrom;
					// now find image
					string imagePath = ((Document.Image)doc.dic[image]).init_from.Uri.LocalPath;
					Int32 idxTexture = CreateTexture(imagePath);

					Data.Parameter param = new Data.Parameter();
					param.name = Crc.GetInstance().GenerateCrc("g_AmbientTexture");
					param.type = Crc.GetInstance().GenerateCrc("Texture");
					param.idxInitData = idxTexture;
					materialParams.Add(param);
				}
				else if (shader.ambient is Document.Color)
				{
					Document.Color color = (Document.Color)shader.ambient;
					Data.Parameter param = new Data.Parameter();
					param.name = Crc.GetInstance().GenerateCrc("g_AmbientColour");
					param.type = Crc.GetInstance().GenerateCrc("Float4");
					float[] floats = { color.floats[0], color.floats[1], color.floats[2], color.floats[3] };
					param.idxInitData = renderData.AddBoxedObjects(ByteConversion.ToByteArray(floats));
					materialParams.Add(param);
					requiredShaderBits.Add("#define DEF_AMBIENT_FIXED");
				}

				if (shader.emission is Document.Texture)
				{
					Document.Texture tex = (Document.Texture)shader.emission;
					String textureChannel = boundGeometryRecord.bindings[tex.texcoord].Value;
					requiredShaderBits.Add("#define DEF_EMISSION_TEXTURE");
					requiredShaderBits.Add("#define DEF_ENABLE_" + textureChannel);
					requiredShaderBits.Add("#define EMISSION_TEXTURE_TEXCOORD TEX_" + textureChannel);

					string sampler = ((Document.Texture)shader.emission).texture;
					string surface = ((Document.Sampler2D)profile.newParams[sampler].param).source;
					string image = ((Document.Surface)profile.newParams[surface].param).initFrom;
					// now find image
					string imagePath = ((Document.Image)doc.dic[image]).init_from.Uri.LocalPath;
					Int32 idxTexture = CreateTexture(imagePath);

					Data.Parameter param = new Data.Parameter();
					param.name = Crc.GetInstance().GenerateCrc("g_EmissionTexture");
					param.type = Crc.GetInstance().GenerateCrc("Texture");
					param.idxInitData = idxTexture;
					materialParams.Add(param);
				}
				else if (shader.emission is Document.Color)
				{
					Document.Color color = (Document.Color)shader.emission;
					Data.Parameter param = new Data.Parameter();
					param.name = Crc.GetInstance().GenerateCrc("g_EmissiveColour");
					param.type = Crc.GetInstance().GenerateCrc("Float4");
					float[] floats = { color.floats[0], color.floats[1], color.floats[2], color.floats[3] };
					param.idxInitData = renderData.AddBoxedObjects(ByteConversion.ToByteArray(floats));
					materialParams.Add(param);
					requiredShaderBits.Add("#define DEF_EMMISION_FIXED");
				}

				if (shader.specular is Document.Texture)
				{
					Document.Texture tex = (Document.Texture)shader.specular;
					String textureChannel = boundGeometryRecord.bindings[tex.texcoord].Value;
					requiredShaderBits.Add("#define DEF_SPECULAR_TEXTURE");
					requiredShaderBits.Add("#define DEF_ENABLE_" + textureChannel);
					requiredShaderBits.Add("#define SPECULAR_TEXTURE_TEXCOORD TEX_" + textureChannel);

					string sampler = ((Document.Texture)shader.specular).texture;
					string surface = ((Document.Sampler2D)profile.newParams[sampler].param).source;
					string image = ((Document.Surface)profile.newParams[surface].param).initFrom;
					// now find image
					string imagePath = ((Document.Image)doc.dic[image]).init_from.Uri.LocalPath;
					Int32 idxTexture = CreateTexture(imagePath);

					Data.Parameter param = new Data.Parameter();
					param.name = Crc.GetInstance().GenerateCrc("g_SpecularTexture");
					param.type = Crc.GetInstance().GenerateCrc("Texture");
					param.idxInitData = idxTexture;
					materialParams.Add(param);
				}
				else if (shader.specular is Document.Color)
				{
					Document.Color color = (Document.Color)shader.specular;
					Data.Parameter param = new Data.Parameter();
					param.name = Crc.GetInstance().GenerateCrc("g_SpecularColour");
					param.type = Crc.GetInstance().GenerateCrc("Float4");
					float[] floats = { color.floats[0], color.floats[1], color.floats[2], color.floats[3] };
					param.idxInitData = renderData.AddBoxedObjects(ByteConversion.ToByteArray(floats));
					materialParams.Add(param);
					requiredShaderBits.Add("#define DEF_SPECULAR_FIXED");
				}

				if (shader.transparency is Document.Texture)
				{
					Document.Texture tex = (Document.Texture)shader.transparency;
					String textureChannel = boundGeometryRecord.bindings[tex.texcoord].Value;
					requiredShaderBits.Add("#define DEF_TRANSPARENCY_TEXTURE");
					requiredShaderBits.Add("#define DEF_ENABLE_" + textureChannel);
					requiredShaderBits.Add("#define TRANSPARENCY_TEXTURE_TEXCOORD TEX_" + textureChannel);

					string sampler = ((Document.Texture)shader.transparency).texture;
					string surface = ((Document.Sampler2D)profile.newParams[sampler].param).source;
					string image = ((Document.Surface)profile.newParams[surface].param).initFrom;
					// now find image
					string imagePath = ((Document.Image)doc.dic[image]).init_from.Uri.LocalPath;
					Int32 idxTexture = CreateTexture(imagePath);

					Data.Parameter param = new Data.Parameter();
					param.name = Crc.GetInstance().GenerateCrc("g_TransparencyTexture");
					param.type = Crc.GetInstance().GenerateCrc("Texture");
					param.idxInitData = idxTexture;
					materialParams.Add(param);
				}
				else if (shader.transparency is Document.Float)
				{
					Document.Float color = (Document.Float)shader.transparency;
					Data.Parameter param = new Data.Parameter();
					param.name = Crc.GetInstance().GenerateCrc("g_Transparency");
					param.type = Crc.GetInstance().GenerateCrc("Float");
					float[] floats = { color.theFloat };
					param.idxInitData = renderData.AddBoxedObjects(ByteConversion.ToByteArray(floats));
					materialParams.Add(param);
					requiredShaderBits.Add("#define DEF_TRANSPARENCY_FIXED");
				}

				newRenderMethod.idxGpuProgram = CreatePhongGpuProgram(requiredShaderBits);
				newMaterial.idxFirstRenderMethod = renderData.AddObject(newRenderMethod);
				newMaterial.numRenderMethods = 1;

				newMaterial.idxFirstParameter = renderData.AddObjects(materialParams);
				newMaterial.numParameters = (UInt32)materialParams.Count;
			}

			return idxMaterial;
		}

		public Int32 CreatePhongGpuProgram(List<String> shaderBits)
		{
			Int32 idxGpuProgram = -1;
			String stdout = "";

			String shaderName = "";
			foreach (String s in shaderBits)
			{
				shaderName += s;
			}
			UInt32 shaderCRC = Crc.GetInstance().GenerateCrc(shaderName);

			if (gpuProgramCache.ContainsKey(shaderCRC))
			{
				idxGpuProgram = gpuProgramCache[shaderCRC];
			}
			else
			{
				shaderBits.Add("#include \"Z:\\Media\\BuildSource\\Shaders\\Collada\\Phong.hlsl\"");

				String inputFilename = Path.GetTempFileName();
				File.WriteAllLines(inputFilename, shaderBits.ToArray());

				String outputFilename = Path.GetTempFileName();
				Process p = new Process();
				p.StartInfo.FileName = @"Z:\ToolChain\Bin\GpuProgramCompiler.exe";
				p.StartInfo.Arguments = String.Format("/OUT:\"{0}\" /IN:\"{1}\" /PLATFORM:{2}", outputFilename, inputFilename, platform);
				p.StartInfo.CreateNoWindow = true;
				p.StartInfo.UseShellExecute = false;
				p.StartInfo.RedirectStandardOutput = true;
				p.Start();
				p.WaitForExit();
				stdout = p.StandardOutput.ReadToEnd();

				if (p.ExitCode == 0)
				{
					Byte[] shader = File.ReadAllBytes(outputFilename);
					Data.GpuProgram newGpuProgram = new Data.GpuProgram();
					newGpuProgram.name = shaderCRC;
					newGpuProgram.numInitDataBytes = (UInt32)shader.Length;
					newGpuProgram.idxInitDataBytes = renderData.AddBoxedObjects(shader);
					idxGpuProgram = renderData.AddObject(newGpuProgram);
					gpuProgramCache[shaderCRC] = idxGpuProgram;
				}

				File.Delete(inputFilename);
				File.Delete(outputFilename);
			}

			if (idxGpuProgram == -1)
				throw new Exception("Failed to compile shader:\n" + stdout);

			return idxGpuProgram;
		}

		public Int32 CreateTexture(String filename)
		{
			Int32 idxTexture = -1;
			UInt32 hsTextureFilename = Crc.GetInstance().GenerateCrc(filename);
			if (textureCache.ContainsKey(hsTextureFilename))
			{
				idxTexture = textureCache[hsTextureFilename];
			}
			else
			{
				//System.Console.WriteLine("Packaging Texture: " + filename);
				if (!File.Exists(filename))
					throw new FileNotFoundException("Couldn't find referenced source texture: " + filename);

				String tmpDir = Path.GetTempPath();

				Process p = new Process();
				p.StartInfo.FileName = @"Z:\ToolChain\Bin\texconv.exe";
				p.StartInfo.Arguments = "-f DXT5 -ft DDS -o " + tmpDir + " " + filename;
				p.StartInfo.UseShellExecute = false;
				p.StartInfo.CreateNoWindow = true;
				p.Start();
				p.WaitForExit();
				if (p.ExitCode != 0)
					throw new Exception("Failed to convert " + filename);

				String tmpFilename = tmpDir + Path.GetFileNameWithoutExtension(filename) + ".dds";

				if (!File.Exists(tmpFilename))
					throw new FileNotFoundException("Couldn't find converted texture: " + tmpFilename);

				Byte[] texBytes = File.ReadAllBytes(tmpFilename);
				File.Delete(tmpFilename);

				Data.Texture newTex = new Data.Texture();
				newTex.numInitDataBytes = (UInt32)texBytes.Length;
				newTex.idxInitData = renderData.AddBoxedObjects(texBytes);
				idxTexture = renderData.AddObject(newTex);

				textureCache[hsTextureFilename] = idxTexture;
			}
			return idxTexture;
		}

		String CrappyRemap(String src)
		{
			if (src.ToLower().Contains("channel"))
			{
				return src.ToLower().Replace("channel", "TEXCOORD");
			}
			throw new Exception("Texture channel remapping completely failed!");
		}
	}
}